// (function () {

//   'use strict';

//   /**
//    * SimpleUndo is a very basic javascript undo/redo stack for managing histories of basically anything.
//    *
//    * options are: {
//    * 	* `provider` : required. a function to call on `save`, which should provide the current state of the historized object through the given "done" callback
//    * 	* `maxLength` : the maximum number of items in history
//    * 	* `onUpdate` : a function to call to notify of changes in history. Will be called on `save`, `undo`, `redo` and `clear`
//    * }
//    *
//    */
//   var SimpleUndo = function (options) {

//     var settings = options ? options : {};
//     var defaultOptions = {
//       provider: function () {
//         throw new Error("No provider!");
//       },
//       maxLength: 30,
//       onUpdate: function () { }
//     };

//     this.provider = (typeof settings.provider != 'undefined') ? settings.provider : defaultOptions.provider;
//     this.maxLength = (typeof settings.maxLength != 'undefined') ? settings.maxLength : defaultOptions.maxLength;
//     this.onUpdate = (typeof settings.onUpdate != 'undefined') ? settings.onUpdate : defaultOptions.onUpdate;

//     this.initialItem = null;
//     this.clear();
//   };

//   function truncate(stack, limit) {
//     while (stack.length > limit) {
//       stack.shift();
//     }
//   }

//   SimpleUndo.prototype.initialize = function (initialItem) {
//     this.stack[0] = initialItem;
//     this.initialItem = initialItem;
//   };


//   SimpleUndo.prototype.clear = function () {
//     this.stack = [this.initialItem];
//     this.position = 0;
//     this.onUpdate();
//   };

//   SimpleUndo.prototype.save = function () {
//     this.provider(function (current) {
//       if (this.position >= this.maxLength) truncate(this.stack, this.maxLength);
//       this.position = Math.min(this.position, this.stack.length - 1);

//       this.stack = this.stack.slice(0, this.position + 1);
//       this.stack.push(current);
//       this.position++;
//       this.onUpdate();
//     }.bind(this));
//   };

//   SimpleUndo.prototype.undo = function (callback) {
//     if (this.canUndo()) {
//       var item = this.stack[--this.position];
//       this.onUpdate();

//       if (callback) {
//         callback(item);
//       }
//     }
//   };

//   SimpleUndo.prototype.redo = function (callback) {
//     if (this.canRedo()) {
//       var item = this.stack[++this.position];
//       this.onUpdate();

//       if (callback) {
//         callback(item);
//       }
//     }
//   };

//   SimpleUndo.prototype.canUndo = function () {
//     return this.position > 0;
//   };

//   SimpleUndo.prototype.canRedo = function () {
//     return this.position < this.count();
//   };

//   SimpleUndo.prototype.count = function () {
//     return this.stack.length - 1; // -1 because of initial item
//   };

//   //exports
//   // node module
//   if (typeof module != 'undefined') {
//     module.exports = SimpleUndo;
//   }

//   // browser global
//   if (typeof window != 'undefined') {
//     window.SimpleUndo = SimpleUndo;
//   }

// })();

export interface SimpleUndoOptions {
  provider: (done: (data: string) => void) => void;
  maxLength: number;
  onUpdate: () => void;
}

export type HistoryStackItem = string | null;

function truncate(stack: Array<HistoryStackItem>, limit: number) {
  while (stack.length > limit) {
    stack.shift();
  }
}

// 管理formrender过程中的数据
export default class SimpleUndo {
  provider: SimpleUndoOptions['provider'];
  maxLength: SimpleUndoOptions['maxLength'];
  onUpdate: SimpleUndoOptions['onUpdate'];
  position: number;
  initialItem: HistoryStackItem;
  stack: Array<HistoryStackItem>;
  constructor(options?: Partial<SimpleUndoOptions>) {
    const mergeOptions = {
      provider: function () {
        throw new Error("No provider!");
      },
      maxLength: 30,
      onUpdate: function () { },
      ...options
    };

    this.provider = mergeOptions.provider;
    this.maxLength = mergeOptions.maxLength;
    this.onUpdate = mergeOptions.onUpdate;
    this.initialItem = null;
    this.stack = [];
    this.position = 0;
    this.clear = this.clear.bind(this);
    this.count = this.count.bind(this);
    this.canUndo = this.canUndo.bind(this);
    this.canRedo = this.canRedo.bind(this);
    this.redo = this.redo.bind(this);
    this.undo = this.undo.bind(this);
    this.save = this.save.bind(this);
    this.initialize = this.initialize.bind(this);
    this.clear();
  }

  count() {
    return this.stack.length - 1; // -1 because of initial item
  };

  canUndo() {
    return this.position > 0;
  };

  canRedo() {
    return this.position < this.count();
  };

  redo(callback: (data: HistoryStackItem) => void) {
    if (this.canRedo()) {
      var item = this.stack[++this.position];
      this.onUpdate();

      if (callback) {
        callback(item);
      }
    }
  }

  undo(callback: (data: HistoryStackItem) => void) {
    if (this.canUndo()) {
      var item = this.stack[--this.position];
      this.onUpdate();

      if (callback) {
        callback(item);
      }
    }
  }

  save() {
    this.provider((current: string) => {
      if (this.position >= this.maxLength) truncate(this.stack, this.maxLength);
      this.position = Math.min(this.position, this.stack.length - 1);
      this.stack = this.stack.slice(0, this.position + 1);
      this.stack.push(current);
      this.position++;
      this.onUpdate();
    });
  }

  clear() {
    this.stack = [this.initialItem];
    this.position = 0;
    this.onUpdate();
  }

  initialize(initialItem: any) {
    this.stack[0] = initialItem;
    this.initialItem = initialItem;
  }
}
